package Day_0220.Redis;

/**
 * @author zxc
 * @date 2023/02/20 17:42
 **/
public class Redis_Caused_Problems {
    public static void main(String[] args) {
        /**
         * 谈一谈对于缓存穿透，缓存击穿和缓存雪崩的理解
         * ===》
         * 一，缓存雪崩
         * 即，高并发情况下，缓存在同一时刻失效，从而导致大量请求打到数据库上，使得数据库宕机。
         * ===》
         * 1.对于系统 A，假设每天高峰期每秒 5000 个请求，本来缓存在高峰期可以扛住每秒 4000 个请求，
         * 但是缓存机器意外发生了全盘宕机（即，缓存在同一时刻全部失效，导致大量请求直接访问数据库;）。
         * 2.缓存挂了，此时 1 秒 5000 个请求全部落数据库，数据库必然扛不住，它会报一下警，然后就挂了。
         * 此时，如果没有采用什么特别的方案来处理这个故障，DBA 很着急，重启数据库，但是数据库立马又被新的流量给打死了。
         *
         * 3.缓存雪崩的解决方案 ：
         * 1）事前：
         * Redis 高可用，主从+哨兵，Redis cluster，避免全盘崩溃。
         * ===》
         * 即，当redis中某个主节点宕机后，则哨兵节点会立刻选取另一个新的节点，来作为新的redis主节点;
         *
         * 2）事中：
         * 本地 ehcache 缓存 + hystrix 限流&降级，避免 MySQL 被打死。
         * ===》
         * （1）ehcache缓存，即为本地缓存，客户端请求到达后，会优先查询ehcache本地缓存中数据;
         * （2）hystrix 限流 && 降级，即限流组件，限制每秒所能够进行处理的请求;
         *  同时，对于未能够进行处理的请求，直接进行服务降级处理，即返回默认值/空值/提示;
         *
         * 3）事后：
         * Redis 持久化，一旦重启，自动从磁盘上加载数据，快速恢复缓存数据。
         * ===》
         * 即，若是redis宕机之后，再次重启redis，需要将redis宕机之前所保存的数据进行恢复;
         *
         * 4）总结 ：
         * ===》
         * 为解决缓存雪崩问题 ：
         * （1）首先，对于redis采用主从架构 + 哨兵节点 + redis集群;
         *  a.主从架构 ：即，使得主节点负责redis的数据写入操作 && 从节点负责redis的数据读取操作，分工协作，提高利用率;
         *  b.哨兵节点 ：即，在主节点宕机之后，哨兵节点会投票选举出 某个从节点作为主节点，从而保证redis的高可用性;
         *  c.redis集群 ：即，若是使用redis单机的话，所能存储的数据是有限的，
         *   但是若是采用redis集群的话，则在一定程度上极大地提高了redis所能存储数据的容量。
         *
         * （2）在高并发场景下，采用 ehcache本地缓存 + 限流 + 服务降级机制;
         *  a.ehcache本地缓存 ：即，客户的请求，首先访问ehcache本地缓存 -> redis;
         *  b.限流 ：即，限制 单位时间内所能够访问数据库的客户端请求;
         *  ===》 保证后台数据库是不会因为高并发的访问请求，从而导致后台数据库出现宕机的情况;
         *  c.服务降级 ：即，高并发情况下，对于某些客户端请求的处理，选择返回 默认值/空值等等;
         *
         * （3）redis采用RBD + AOF方法，来进行数据持久化
         *  即，保证了redis中数据在redis重启时，能够恢复到redis关闭之前的数据;
         *  a.RDB文件，即记录了当时redis中的全部记录。
         *  （即，类似于快照snapshot，记录某一时刻下redis中所有数据实例;）
         *  b.AOF文件，即记录了redis所执行过的所有命令，以append-only方式即可;
         *  （即，直接在原先的AOF文件下，进行命令追加即可;）
         *
         * 二，缓存穿透
         * 1.定义
         * 在高并发情况下，大量客户端请求是redis && 数据库中皆不存在的数据，
         * 即，大量的客户端请求都需要去直接访问数据库，从而导致数据库压力陡增，导致数据库宕机;
         * ===》
         * 解决缓存穿透的核心，在于如何判断客户端请求访问的数据是否存在于 数据库中;
         *
         * 2.缓存穿透的解决方法 ：
         * （1）存储空值 ：
         *  每次系统 A 从数据库中只要没查到，就写一个空值到缓存里去，比如 ：set -999 UNKNOWN 。
         *  然后设置一个过期时间，这样的话，下次有相同的 key 来访问的时候，在缓存失效之前，都可以直接从缓存中取数据。
         * （2）布隆过滤器 ：
         *  在某些场景（如数据量大的博客）下不优雅，还可能会缓存过多的空值，更加优雅的方式就是：使用 bitmap 布隆过滤
         *
         * 三，缓存击穿
         * 1.缓存击穿的概念
         * 缓存击穿，就是说某个 key 非常热点，访问非常频繁，处于集中式高并发访问的情况，
         * 当这个 key 在失效的瞬间，大量的请求就击穿了缓存，直接请求数据库。
         * ===》
         * （1）即，缓存击穿是针对于热点key数据的，大量请求访问热点key数据，
         *  在 热点key数据对应的缓存失效 && 重建热点key所对应的缓存的过程中，大量请求直接访问数据库,导致数据库宕机;
         * （2）解决缓存穿透的核心，在于限制 热点缓存失效后，重建热点缓存的客户端请求的数量;
         *
         * 2.不同场景下的解决方式：
         * （1）设置缓存永久不过期 ：
         *  若缓存的数据是基本不会发生更新的，则可尝试将该热点数据设置为永不过期。
         *  ===》
         *  适用于redis中数据体量较小的情况
         *
         * （2）分布式锁，限制所能够进行缓存重建的客户端请求 ：
         *  若缓存的数据更新不频繁，且缓存刷新的整个流程耗时较少的情况下，
         *  则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁，或者本地互斥锁，
         *  以保证仅少量的请求能请求数据库并重新构建缓存，其余线程则在锁释放后能访问到新缓存。
         *  ===》
         *  即，在高并发情况下，为避免 热点key所对应的缓存失效后，导致的缓存击穿问题，
         *  使得大量缓存重建的请求 尝试获取分布式锁，有且仅有获取到分布式锁的请求，才能够去访问数据库 && 重建热点缓存;
         *
         * （3）若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下，
         *  可以利用定时线程在缓存过期前，主动地重新构建缓存或者延后缓存的过期时间，以保证所有的请求能一直访问到对应的缓存。
         *  ===》
         *  1）若是缓存重建操作耗时较长的话，则选择创建定时线程，对于 即将要失效的缓存进行缓存重建，从而保证缓存永不失效;
         *  2）问题在于，若是redis中某些缓存长时间不被使用，但是由于该机制的存在，导致redis中缓存一直不失效，
         *  从而导致了redis中数据量过大 && 内存溢出;
         *
         * 四，缓存击穿 和 缓存穿透这两者的区别：
         * （1）缓存击穿重点在“击” ：
         *  就是某个或者是几个热点 key 穿透了缓存层;
         *  ===》
         *  即，在高并发情况下，针对于热点key数据，在其失效的一瞬间，大量请求直接访问数据库;
         *
         * （2）缓存穿透重点在“透” ：
         *  大量的请求绕过了缓存层
         *  ===》
         *  即，在高并发情况下，访问 redis && 数据库中皆不存在的数据，从而使得这些请求都直接打到数据库上;
         *
         */
    }
}
